package InternetHax;

import InternetHax.App;
import java.util.Random;
import java.util.Vector;

import javax.microedition.lcdui.Canvas;
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.Image;
import javax.microedition.lcdui.game.GameCanvas;
import javax.microedition.lcdui.game.Sprite;
import javax.microedition.lcdui.game.TiledLayer;

/**
 *
 * @author LoginError
 */
/**
 * HaxCanavas, essentially the graphics manager for the game, this canvas
 * is the entire game screen and any drawing is done in it's paint method
 *
 * <br><br>CAUTION: This file contains hacks, Hax, poorly written code and strange comments
 * parental discretion is advised.
 *
 * @author LoginError
 *
 *
 */
public class HaxCanvas extends GameCanvas implements Runnable {

    private TiledLayer mapLayer = null;
    private Image tilemapImage;
    private Image heroImage;
    //private Image fooImage;
    ///private int[] inputQueue = new int[2];
    private Queue inputBuffer = new Queue();
    private boolean running = true;
    private Point2d start = new Point2d(0, 0);
    private Point2d mapOffset = new Point2d(0, 0);
    private Point2d debugTile = new Point2d(0, 0);
    private MapFactory factory;
    private Hero theHero;
    private Monster[] fooArray;
    private int DYNAMIC_TILE_OFFSET = 5;
    //private Font59 smallFont;
    private Image debugCursor;
    private Image bluCursor;
    private Image background;
    private Image fourWay;
    private Image eightWay;
    private Image numpad;
    private Sprite[] appImages;
    private AppFactory appFactory;
    private FPSCounter frameCounter;
    private Image floppyDisk;

    
    static int bgColor = 0x000000;
    ResourceManager resManager;
    
    public HaxCanvas() {
        super(false);
    }

    
    
    public void shutdown()
    {
        Toolbox.printDebug("Shutting down!");
        resManager.unloadResource("Failtiles3.gif");
        resManager.unloadResource("GenaricHeroMan.gif");
        resManager.unloadResource("Cursor.gif");
        resManager.unloadResource("BluCursor.gif");
        resManager.unloadResource("TestBackground.gif");
        resManager.unloadResource("4wayArrow.gif");
        resManager.unloadResource("8wayArrow.gif");
        resManager.unloadResource("Numpad.gif");
        Gamestate.midlet.notifyDestroyed();
    }

    public void init() {

        try {
            //intialized Gamestate

            Gamestate.font = new Font59("/Terminal.fnt");
            Gamestate.fontColor = Gamestate.font.makeNewFont(Gamestate.uiFontColor);

            Gamestate.state = Constants.STATE_LOADING;

            Gamestate.gameManager = new GameManager();
            resManager = new ResourceManager();
            Gamestate.resourceManager = resManager;
            
            tilemapImage = resManager.getResourceByName("Failtiles3.gif");
            heroImage = resManager.getResourceByName("GenaricHeroMan.gif");
            debugCursor = resManager.getResourceByName("Cursor.gif");
            bluCursor = resManager.getResourceByName("BluCursor.gif");
            background = resManager.getResourceByName("TestBackground.gif");
            floppyDisk = resManager.getResourceByName("FloppyDisk.gif");
            fourWay = resManager.getResourceByName("4wayArrow.gif");
            eightWay = resManager.getResourceByName("8wayArrow.gif");
            numpad = resManager.getResourceByName("Numpad.gif");

            appImages = new Sprite[2];


            Image activeImage = resManager.getResourceByName("activeApps.png");
            appImages[0] = new Sprite(activeImage,16,16);
            Image consumableImage = resManager.getResourceByName("consumableApps.png");
            appImages[1]= new Sprite(consumableImage,16,16);
            frameCounter = new FPSCounter();

            Gamestate.menu = new TextMenu();
            Gamestate.invMenu = new Menu9(appImages);
            Gamestate.dirMenu = new DirectionMenu(fourWay,eightWay);
            
            appFactory = new AppFactory();
            

            factory = new MapFactory(appFactory);
            factory.makeRandomMap(60, 60);
            Gamestate.theMap = factory.getFinishedMap();
            Gamestate.debugTiles = new Vector();

            //every game needs an hero
            theHero = new Hero(new Sprite(heroImage, 16, 16));
            theHero.setHP(100);
            theHero.setName("The Hero");

            Gamestate.theHero = theHero;
            
            Gamestate.gameManager.addNewActor(theHero);
            Gamestate.appManager = new AppManager();
            App[] equippedApps = new App[9];            
            equippedApps[3] = new ActiveApp(Constants.APP_DEBUGSHOTGUN);
            ActiveApp active = new ActiveApp(Constants.APP_SIMPLEPUNCH);
            active.isRecharging = true;
            equippedApps[1] = active;
            equippedApps[4] = new ActiveApp(Constants.APP_LASERGUN);
            ConsumableApp healPack = new ConsumableApp(Constants.APP_HEALPACK);
            healPack.uses = 2;
            equippedApps[6] = healPack;
            equippedApps[8] = new ActiveApp(Constants.APP_SELFDESTRUCT);
            
            Gamestate.theHero.setEquippedApps(equippedApps);
            

            //important: set fullscreen BEFORE getting and storing the screen size
            setFullScreenMode(true);
            Gamestate.screenWidth = getWidth();
            Gamestate.screenHeight = getHeight();
            Gamestate.centerScreen = new Point2d(getWidth() / 2, getHeight() / 2);
            Gamestate.generator = new Random();
            Gamestate.console = new Console(Gamestate.screenWidth, 40, 6, 0);
            Gamestate.console.print("Welcome to InternetHax ");

            refreshTileMap();

            theHero.setTilePosition(new Point2d(start.x, start.y));
            jumpCameraToTile(theHero.getTilePosition());
            

            
            Gamestate.state = Constants.STATE_PLAYERTURN;
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    protected void keyRepeated(int key) {
        keyPressed(key);
    }

    protected void keyPressed(int key) {
        /*
         * Since the constants for the numberkeys and the constants returned by getGameAction
         * do not overlap (I looked it up), they are both stored in keyAction. That is to say key action will
         * always contain EITHER a key code or a game action.
         */
         int keyAction;
        /*if (keyAction == 0 || (GAME_A <= keyAction && keyAction <= GAME_D) ) { //if the key has no gameaction or if it's one of A,B,C,D, ignore it and store the keycode
         keyAction = key;
         }*/
        if ((key >= KEY_NUM0 && key <= KEY_NUM9) || key == KEY_POUND || key == KEY_STAR)
           keyAction = key;
        else
           keyAction = getGameAction(key);

        //Toolbox.printDebug("Key " +keyAction + " was pressed");
        //if there's a menu then the game is waiting for it, so just deal with the menu
        if (Gamestate.modalMenu != null) {
            Gamestate.modalMenu.handleInput(keyAction);
        }
        else {
            inputBuffer.put(new Integer(keyAction));
        }
    }

    /**
     * Builds a TiledLayer from theMap, should only be called if the
     * layout of theMap has changed, as it iterates through the entire map.
     */
    public void refreshTileMap() {
        if (Gamestate.theMap == null) {
            return;
        }
        mapLayer = null;
        TiledLayer tempLayer = new TiledLayer(Gamestate.theMap.xsize, Gamestate.theMap.ysize, tilemapImage, 16, 16);

        for (int y = 0; y < tempLayer.getRows(); y++) {
            for (int x = 0; x < tempLayer.getColumns(); x++) {
                if (Gamestate.theMap.getTileType(x, y) == Constants.TILE_PLAYER_START) {
                    tempLayer.setCell(x, y, 3);
                    start = new Point2d(x, y);
                }
                else if (Gamestate.theMap.getTileType(x, y) == Constants.TILE_PLAYER_GOAL) {
                    tempLayer.setCell(x, y, 4);
                }
                else if (Gamestate.theMap.isWalkable(x, y)) {
                    tempLayer.setCell(x, y, getDynamicTileID(x, y));
                }
                else {
                    tempLayer.setCell(x, y, 0); //there's nothing there  
                }
            }
        }
        mapLayer = tempLayer;
    }
    
    public void mainMenu()
    {       
        String[] choices = {"Stats", "Inventory", "Settings","NetDex", "Quit Game","Exit"};
        boolean done = false;
        while(!done){
        switch (Gamestate.menu.pickOne("Menu", choices))
        {
            case 0:
                String [] stats = new String[1];
                stats[0] = "Ok";
                while(!done){
                    switch(Gamestate.menu.pickOne("Stats: You are Awesome", stats))
                    {
                        case 0:
                            done = true;
                        break;
                    }
                }
                done = false;
            break;

            case 1:
                Gamestate.invMenu.getApp();
            break;
            
            case 2:
                
                while(!done){
                    String[] settings = new String[6];
                    settings[0] = "Invert numpad = "+Gamestate.invertNumpad ;
                    settings[1] = "Debug Twitter = " +Gamestate.printDebug;
                    settings[2] = "Detail color = " + Integer.toHexString(Gamestate.uiDetailColor);
                    settings[3] = "Window color = " + Integer.toHexString(Gamestate.uiBackgroundColor);
                    settings[4] = "Font color   = " + Integer.toHexString(Gamestate.uiFontColor);
                    settings[5] = "Back";
                
                    switch(Gamestate.menu.pickOne("Settings", settings))
                    {
                        case 0:
                            Gamestate.invertNumpad = Gamestate.menu.onOff("Invert Numpad", Gamestate.invertNumpad);
                        break;

                        case 1:
                            Gamestate.printDebug = Gamestate.menu.onOff("Verbose debugging", Gamestate.printDebug);
                        break;

                        case 2:
                            ColorMenu colorPicker = new ColorMenu();
                            Gamestate.console.print(colorPicker.getColor(0)+ "");
                        break;

                        case 5:
                            done = true;
                        break;
                    }
                }
                done = false;
            break;

            case 3:
                Gamestate.state = Constants.STATE_DEBUG_CURSOR;
                debugTile = new Point2d(theHero.tilePosition);
                done = true;
            break;
            
            case 4:
                if (Gamestate.menu.yesNo("Are you sure?")) {
                    Gamestate.running = false;
                    done = true;
                }
            break;

            case 5:
                done = true;
            break;
            }
        }
    }
    
    public void useAppMenu(){
            App gotApp = Gamestate.invMenu.getApp();
            if(gotApp != null)
                Gamestate.appManager.useApp(theHero, gotApp,-1);
    }
    
    
    public void contextButton()
    {
        Point2d heroPos = theHero.getTilePosition();
        int tileID = Gamestate.theMap.getTileType(heroPos.x, heroPos.y);
        switch(tileID)
        {
            case Constants.TILE_PLAYER_GOAL:
                if(Gamestate.menu.yesNo("Go to the next floor?"))
                {
                    Gamestate.state = Constants.STATE_LOADING;
                    Gamestate.gameManager.reset();
                    factory.makeRandomMap(60, 60);
                    Gamestate.theMap = factory.getFinishedMap();
                    refreshTileMap();
                    theHero.setTilePosition(start);
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException ex) {
                        ex.printStackTrace();
                    }
                    Gamestate.gameManager.addNewActor(theHero);
                    Gamestate.theMap.setActorOnTile(theHero, heroPos.x, heroPos.y);
                    Gamestate.state = Constants.STATE_PLAYERTURN;
                }
            break;


            default:
                theHero.setAP(0); //skip turn
            break;
        }
        

    }

    public void handleInput(int keyAction) {

        

        if(Gamestate.state == Constants.STATE_PLAYERTURN){
            switch(keyAction)
            {
                case KEY_POUND:
                    mainMenu();
                break;

                case KEY_NUM5:
                    useAppMenu();
                break;

                case DOWN:
                    theHero.move(Constants.DIRECTION_DOWN);
                    theHero.loseAP(1);
                break;

                case RIGHT:
                    theHero.move(Constants.DIRECTION_RIGHT);
                    theHero.loseAP(1);
                break;

                case LEFT:
                    theHero.move(Constants.DIRECTION_LEFT);
                    theHero.loseAP(1);
                break;

                case UP:
                    theHero.move(Constants.DIRECTION_UP);
                    theHero.loseAP(1);
                break;

                case FIRE:
                    contextButton();
                break;

            }
            jumpCameraToTile(theHero.getTilePosition());
        }

        
        if(Gamestate.state == Constants.STATE_DEBUG_CURSOR)
        {
            switch(keyAction)
            {
            case DOWN:
                debugTile.add(0, 1);
                jumpCameraToTile(debugTile);
            break;
            
            case RIGHT:
                debugTile.add(1, 0);
                jumpCameraToTile(debugTile);
            break;
            
            case LEFT:
                debugTile.add(-1, 0);
                jumpCameraToTile(debugTile);
            break;
            
            case UP:
                debugTile.add(0, -1);
                jumpCameraToTile(debugTile);
            break;

            case FIRE:
                Gamestate.console.print("Debug info printed System.out");
                Gamestate.theMap.debugTile(debugTile.x, debugTile.y);
            break;

            case KEY_NUM0:
                Gamestate.state = Constants.STATE_PLAYERTURN;
                jumpCameraToTile(theHero.tilePosition);
            break;
            }
        }
    }

    /**
     * Gameloop, it is made of loop and game
     */
    public void gameLoop() {
        Actor currentActor = null;
        Integer keypressed;
        
        while (Gamestate.running) {

            try {
                if(Gamestate.state == Constants.STATE_GAMEOVER)
                {
                    Gamestate.console.print("Game over");
                    if(Gamestate.menu.yesNo("Game over! Play again?"))
                    {
                    }
                    else
                        break;
                }

                if(Gamestate.state == Constants.STATE_PLAYERTURN)
                    currentActor = Gamestate.gameManager.getNextTurn();
                
                if (!Gamestate.gameManager.isPlayerTurn()) {
                    if (currentActor != null) {
                        currentActor.takeTurn();

                    }
                } else {
                    keypressed = (Integer) inputBuffer.get(); //This will block until the buffer gets a keypress
                    
                    if(Gamestate.state == Constants.STATE_PLAYERTURN){
                        Gamestate.debugTiles.removeAllElements();
                        Gamestate.theHero.takeTurn();
                    }
                    
                    handleInput(keypressed.intValue());
                }
            } catch (Exception e) {
                System.err.println("GAMELOOP THREAD CRASHED!");
                e.printStackTrace();
            }

        }
        shutdown();
    }

    /**
     * Returns a "dynamic tile ID" for any tile on the map, this method checks the neighbours
     * of a tile in order to determine what tile should be used to represent it. This allows
     * the map to have a border around it and gives a smoother looking map.
     * @param x the x coordinate of the tile to be checked, in tiles
     * @param y the y coordinate of the tile to be checked, in tiles
     * @return the id of the tile that should be drawn for this map square
     */
    public int getDynamicTileID(int x, int y) {
        int up = 8;
        int right = 4;
        int down = 2;
        int left = 1;
        int output = 0;

        if (Gamestate.theMap.isWalkable(x, y - 1)) {
            output = output | up;
        }
        if (Gamestate.theMap.isWalkable(x + 1, y)) {
            output = output | right;
        }
        if (Gamestate.theMap.isWalkable(x, y + 1)) {
            output = output | down;
        }
        if (Gamestate.theMap.isWalkable(x - 1, y)) {
            output = output | left;
        }
        return output + DYNAMIC_TILE_OFFSET;
    }

    /**
     * Causes the "camera" to jump to a speified tile, centering it on the screen by changing mapOffset;
     * @param thetile a point in 2d space representing the tile to jump to
     */
    public void jumpCameraToTile(Point2d thetile) {
        if (thetile != null) {
            Point2d mapLocPix = new Point2d((thetile.x * -Constants.TILESIZE_IN_PIXELS ) + (getWidth() / 2), (thetile.y * -Constants.TILESIZE_IN_PIXELS) + (getHeight() / 2));
            mapOffset = thetile;
            mapLayer.setPosition(mapLocPix.x - Constants.TILESIZE_HALF, mapLocPix.y - Constants.TILESIZE_HALF);
        }
    }

    /**
     * Causes the "camera" to jump the start tile, centering on the screen
     */
    public void jumpCameraToStart() {
        if (start != null) {
            Point2d mapLocPix = new Point2d((start.x * -Constants.TILESIZE_IN_PIXELS) + (getWidth() / 2), (start.y * -Constants.TILESIZE_IN_PIXELS) + (getHeight() / 2));
            mapOffset = start;
            mapLayer.setPosition(mapLocPix.x - Constants.TILESIZE_HALF, mapLocPix.y - Constants.TILESIZE_HALF);
        }
    }

    /**
     * Paint method, responsable for painting the entire game, it is called by {@link Painter}
     * via repaint() at a constant interval
     * @param g graphics object for this canvas, for unknown reasons it does not properly
     * draw, so getGraphics is imediatly called within the method.
     */
    public void paint(Graphics g) {

        Graphics graphics = getGraphics();

        switch(Gamestate.state)
        {
            
            

            case Constants.STATE_LOADING:
                graphics.setColor(100, 100, 100);
                graphics.fillRect(0, 0, getWidth(), getHeight());
                //graphics.drawImage(appImage, Gamestate.centerScreen.x, Gamestate.centerScreen.x, 0);
                Gamestate.font.PrintString(graphics, Gamestate.centerScreen.x, Gamestate.centerScreen.y, "Loading....",0,0,Gamestate.fontColor);
            break;

            case Constants.STATE_GAMEOVER:
            case Constants.STATE_DEBUG_CURSOR:
            case Constants.STATE_PLAYERTURN:
            //TODO:Figure out why i need to call getGraphics here instead of using g

            graphics.setColor(0, 0, 0);
            //graphics.setColor(bgColor += Gamestate.generator.nextInt(1000));
            graphics.fillRect(0, 0, getWidth(), getHeight());

            graphics.drawImage(background, 0, 0, 0);

            //dont paint the map if there's no map layer
            if (mapLayer != null) {
                mapLayer.paint(graphics);
            }

            //draw some debug tiles to help me see where all the apps are "Scanning"
            if (Gamestate.debugTiles.size() != 0) {
                for (int i = 0; i < Gamestate.debugTiles.size(); i++) {
                    Point2d tile = (Point2d) Gamestate.debugTiles.elementAt(i);
                    int xpos = (tile.x * Constants.TILESIZE_IN_PIXELS) - (mapOffset.x * Constants.TILESIZE_IN_PIXELS) + Gamestate.screenWidth / 2 - Constants.TILESIZE_HALF;
                    int ypos = (tile.y * Constants.TILESIZE_IN_PIXELS) - (mapOffset.y * Constants.TILESIZE_IN_PIXELS) + Gamestate.screenHeight / 2 - Constants.TILESIZE_HALF;
                    graphics.drawImage(debugCursor, xpos, ypos, 0);
                }
            }

            //draw every actor that the turnManger knows about
            Vector actors = Gamestate.gameManager.getAllTheActors();
            for (int i = 0; i < actors.size(); i++) {
                Actor drawme = (Actor) actors.elementAt(i);
                if(drawme != null)
                    drawme.paint(graphics, mapOffset);
            }

            //draw the console
            Gamestate.console.draw(graphics, Gamestate.font, 0, Gamestate.screenHeight - 40, true);

            //draw the HUD
            Gamestate.console.drawHUD(graphics, 0, Gamestate.screenHeight - 62);

            

            //draw an icon to indicate that computerNumpad is on
            if (numpad != null && Gamestate.invertNumpad) {
                graphics.drawImage(numpad, Gamestate.screenWidth - 18, Gamestate.screenHeight - 60, 0);
            }

            if(Gamestate.state == Constants.STATE_DEBUG_CURSOR)
            {
                Point2d tile = new Point2d(debugTile);
                int xpos = (tile.x * Constants.TILESIZE_IN_PIXELS) - (mapOffset.x * Constants.TILESIZE_IN_PIXELS) + Gamestate.screenWidth / 2 - Constants.TILESIZE_HALF;
                int ypos = (tile.y * Constants.TILESIZE_IN_PIXELS) - (mapOffset.y * Constants.TILESIZE_IN_PIXELS) + Gamestate.screenHeight / 2 - Constants.TILESIZE_HALF;
                graphics.drawImage(bluCursor, xpos, ypos, 0);
            }

            break;
        }
        if (Gamestate.modalMenu != null) {
                Gamestate.modalMenu.paint(graphics, Gamestate.screenWidth / 2, Gamestate.screenHeight / 2);
        }

        frameCounter.tick();
        frameCounter.paint(graphics, Gamestate.screenWidth - 60, Gamestate.screenHeight - 55);

        flushGraphics();
            
            
    }

    public void startGame() {

        Painter paintThread = new Painter();
        paintThread.start();

        GameLoop loopThread = new GameLoop();
        loopThread.start();

    }

    private class Painter extends Thread {

        private long FRAME_TIME = 30; //
        private long prevTimeMillis = 0;
        //paint and then wait until another 1/20'th has passed
        //this caps the framerate at 20fps
        public void run() {
            prevTimeMillis = System.currentTimeMillis();
            while (running) {
                try {
                    repaint();
                    long currentTimeMillis = System.currentTimeMillis();
                    Thread.sleep(FRAME_TIME - (currentTimeMillis - prevTimeMillis));
                    prevTimeMillis = System.currentTimeMillis();
                } catch (Exception ex) {
                    System.err.println("PAINTER THREAD CRASHED!");
                    ex.printStackTrace();
                }
            }
        }
    }

    private class GameLoop extends Thread {

        public void run() {
            gameLoop();
        }
    }

    public void run() {
        long FRAME_TIME = 50; // 1/20'th of a second
        long prevTimeMillis = 0;

        prevTimeMillis = System.currentTimeMillis();
        while (Gamestate.running) {
            try {
                repaint();
                long currentTimeMillis = System.currentTimeMillis();
                Thread.sleep(FRAME_TIME - (currentTimeMillis - prevTimeMillis));
                prevTimeMillis = System.currentTimeMillis();
            } catch (InterruptedException ex) {
                ex.printStackTrace();
            }
        }
    }
}//TODO: TURN THIS INTO A MAP OF SOMESORT
    /*public void drawMapDebug(Map testmap, short xsize, short ysize)
{
if(xsize > getWidth() || ysize > getHeight()){
xsize = (short)getWidth();
ysize = (short)getHeight();
}

for(int y = 0; y < xsize; y++)
{
for(int x = 0; x < ysize; x++)
{
byte thetile = (byte)testmap.getTileType(x, y);
if(thetile == Tile.TILE_FLOOR)
graphics.setColor(246, 249, 199);
else if(thetile == Tile.TILE_CORRUPTED_FLOOR)
graphics.setColor(184, 194, 22);
else if (thetile == Tile.TILE_UNPASSABLE)
graphics.setColor(0,0,244);
else if (thetile == Tile.TILE_CORRUPTED_SPACE)
graphics.setColor(88, 9, 247);
else if (thetile == Tile.TILE_DEBUG_ROOM_CENTER)
graphics.setColor(123,123,123);
else if (thetile == Tile.TILE_PLAYER_START)
graphics.setColor(0,255,0);
else if (thetile == Tile.TILE_PLAYER_GOAL)
graphics.setColor(255,0,0);


graphics.fillRect(x*3, y*3, 3, 3);
}
}
flushGraphics();
}*/
    
